
#pragma once

#include "Runtime.Low/Pipeline.h"
#include "../Parameters/Parameter.h"
#include "../Parameters/SpecializedParameters.h"
#include "../Parameters/ParameterManager.h"

namespace ttg_internal
{
	namespace utils
	{
		///<summary>
		/// Wrapper-class, that encapsulates parameters of the one pipeline node.
		///</summary>
		public ref class Node :public System::ComponentModel::INotifyPropertyChanged
		{
			private:
				System::String ^_name;
				ParameterInt64 ^_activeTaskCount;
				ParameterDouble ^_usage;
				System::Collections::ObjectModel::ObservableCollection<Node ^> ^_outputs;
			private:
				///<summary>
				/// Notifies about local property change.
				///</summary>
				void OnPropertyChange(System::String ^name)
				{ PropertyChanged(this, gcnew PropertyChangedEventArgs(name)); }
				///<summary>
				/// Handler for root property "ActiveTaskCount".
				///</summary>
				void OnRootPropertyChange_ActiveTaskCount(System::Object ^sender,
														  System::ComponentModel::PropertyChangedEventArgs ^e)
				{ OnPropertyChange("ActiveTaskCount"); }
				///<summary>
				/// Handler for root property "Usage".
				///</summary>
				void OnRootPropertyChange_Usage(System::Object ^sender,
											    System::ComponentModel::PropertyChangedEventArgs ^e)
				{ OnPropertyChange("Usage"); }
				///<summary>
				/// Performs initialization of this node.
				/// Should be called from constructores only.
				///</summary>
				void init(System::String ^name,
						  ParameterInt64 ^activeTaskCount,
						  ParameterDouble ^usage)
				{
					_name = name;
					_activeTaskCount = activeTaskCount;
					_activeTaskCount->PropertyChanged
						+= gcnew System::ComponentModel::PropertyChangedEventHandler
							(this, &Node::OnRootPropertyChange_ActiveTaskCount);
					_usage = usage;
					_usage->PropertyChanged
						+= gcnew System::ComponentModel::PropertyChangedEventHandler
							(this, &Node::OnRootPropertyChange_Usage);
					this->_outputs = gcnew System::Collections::ObjectModel::ObservableCollection<Node ^>();
				}
			internal:
				///<summary>
				/// Creates node with required parameters.
				///</summary>
				Node(System::String ^name,
					 ParameterInt64 ^activeTaskCount,
					 ParameterDouble ^usage)
				{
					init(name, activeTaskCount, usage);
				}
				///<summary>
				/// Creates node according to NodeInfo.
				///</summary>
				Node(ttg_internal::pipeline::LiteNodeInfo *node)
				{
					try
					{
						ParameterSet ^name = ParameterManager::getRef()->findSet(node->getName());
						ParameterInt64 ^activeTaskCount
							= safe_cast<ParameterInt64 ^>(
								ParameterManager::getRef()->findParameter(node->getActiveTaskCount()));
						ParameterDouble ^usage
							= safe_cast<ParameterDouble ^>(
								ParameterManager::getRef()->findParameter(node->getUsage()));
						if (name == nullptr ||
							activeTaskCount == nullptr ||
							usage == nullptr)
							throw gcnew System::ApplicationException("Cannot find corresponded parameters");
						init(name->Name, activeTaskCount, usage);
					}
					catch (System::Exception ^ex)
					{
						throw gcnew System::ApplicationException("Cannot deserialize pipeline node", ex);
					}
				}
			public:
				///<summary>
				/// Event for INotifyPropertChanged.
				///</summary>
				virtual event PropertyChangedEventHandler ^PropertyChanged;
				///<summary>
				/// Returns name of the node.
				///</summary>
				property System::String ^Name
				{
					System::String ^get()
					{ return _name; }
				}
				///<summary>
				/// Returns count of active (unperformed) tasks.
				///</summary>
				property int ActiveTaskCount
				{
					int get()
					{ return (int)_activeTaskCount->Int64Value; }
					void set(int value)
					{ _activeTaskCount->Int64Value = value; }
				}
				///<summary>
				/// Returns usage (in percents) of system by this node.
				///</summary>
				property double Usage
				{
					double get()
					{ return _usage->DoubleValue; }
					void set(double value)
					{ _usage->DoubleValue = value; }
				}
				///<summary>
				/// Rerturns outputs of this node.
				///</summary>
				property System::Collections::ObjectModel::ObservableCollection<Node ^> ^Outputs
				{
					System::Collections::ObjectModel::ObservableCollection<Node ^> ^get()
					{ return _outputs; }
				}
		};
	}
}
